/******************************************************************************
** 公司名称：天津柯迪斯科技有限公司
** 版权信息：
** 文件名称： App_Protocol_SIF.c
** 模块名称： SIF一线通协议模块
** 版 本 号： V1.0
** 作    者： 蒲磊（借鉴自网上的一段代码）
** 备    注：采用定时器扫描的方式进行波形解析，读取数据，即利用定时器 + 一个GPIO口进行通讯数据读取
** 修改记录： 版本     修改人      时间         修改内容
**
******************************************************************************/


/******************************************************************************
* 头文件
******************************************************************************/
#include "bsp.h"
#include "App_Protocol_SIF.h"
#include "App_MainTask.h"
#include "App_Parameter.h"
#include "stm32g0xx_ll_gpio.h"
#include "stm32g0xx_ll_tim.h"
/******************************************************************************
*宏定义 ('#define')
******************************************************************************/
#define DATA_REV_PIN            LL_GPIO_IsInputPinSet(GPIOB,LL_GPIO_PIN_7)     //定义数据接收引脚(根据实际项目进行更改)

#define LOW                     0       //低电平
#define HIGH                    1       //高电平

#define SYNC_L_TIME_NUM         1000     //同步信号低电平时间：50ms = 50000us / 50us = 1000
#define SYNC_H_TIME_NUM_MIN     18      //同步信号高电平最小时间：1ms-100us = 900us / 50us = 18  
#define SYNC_H_TIME_NUM_MAX     22      //同步信号高电平最大时间：1ms+100us = 1100us / 50us = 22

#define SHORT_TIME_NUM_MIN      9       //一个逻辑周期中短的时间最小值：0.5ms-50us = 450us / 50us = 9
#define SHORT_TIME_NUM_MAX      11      //一个逻辑周期中短的时间最大值：0.5ms+50us = 550us / 50us = 11

#define LONG_TIME_NUM_MIN       15      //一个逻辑周期中长的时间最小值：1ms-150us = 750us / 50us = 15
#define LONG_TIME_NUM_MAX       23      //一个逻辑周期中长的时间最大值：1ms+150us = 1150us / 50us = 23

#define LOGIC_CYCLE_NUM_MIN     26      //一个逻辑周期最小时间：1.5ms-200us = 1300us / 50us = 26
#define LOGIC_CYCLE_NUM_MAX     34      //一个逻辑周期最大时间：1.5ms+200us = 1700us / 50us = 34

#define HALF_LOGIC_CYCLE_MIN    13      //一个逻辑周期的1/2最小时间：750us-100us = 650us / 50us = 13
#define HALF_LOGIC_CYCLE_MAX    17      //一个逻辑周期的1/2最大时间：750us+100us = 850us / 50us = 17

#define END_SIGNAL_TIME_NUM     100     //结束信号电平时间：5ms低电平 + Nms高电平，实际检测5ms低电平就行，一帧数据发送完成后检测5ms低电平就代表完成了，不发数据的时候上拉电阻拉高了

#define REV_BIT_NUM             8       //接收的bit位个数，看是按字节接收还是按字接收，1字节=8bit，1字=2字节=16bit
#define REV_DATA_NUM            12      //接收的数据个数


/******************************************************************************
*类型定义 ('typedef')
******************************************************************************/
typedef enum
{
    INITIAL_STATE=0,            //初始状态，等待接收同步信号
    SYNC_L_STATE=1,             //接收同步低电平信号状态
    SYNC_H_STATE=2,             //接收同步高电平信号状态
    DATA_REV_STATE=3,           //读取数据码电平状态
    END_SIGNAL_STATE=4,         //接收结束电平信号状态
    RESTART_REV_STATE=5         //接收过程出错重新接收状态
} REV_STATE_e;                  //接收数据状态枚举

/******************************************************************************
* 全局变量定义
******************************************************************************/
uint8_t receive_state       = 0;        //接收数据状态
uint8_t receive_bit_num     = 0;        //接收的bit位个数
uint8_t receive_data_num    = 0;        //接收的数据个数

//接收数据缓存数组-用一个数组来缓存数据，12个数据字节
uint8_t receive_data_buf[REV_DATA_NUM] = {0};
uint32_t  H_L_Level_time_cnt           = 0;     //高低电平时间计数
uint8_t start_H_L_Level_timming_flag   = 0;     //开始高低电平计时标记
uint8_t has_read_bit                   = 0;     //1-已经读取一个bit位
uint8_t check_OK                       = 0;     //1-校验和正确，0-校验和失败
uint8_t read_success                   = 0;     //一帧数据是否读取成功，0-不成功，1-成功

static uint16_t gusOvertimeCnt         = 0;
stBIKE_DATA         gxBikeinfo;

/******************************************************************************
*函数声明 ('static')
******************************************************************************/

static void Receive_Data_Handle(void);     //接收数据处理
static uint8_t Check_Sum_Handle(void);        //校验和处理

/*****************************************************************************
* 函数实现 - global ('extern') and local ('static')
******************************************************************************/

/*******************************************************************************
 *函数名称 : vPROTOCOL_Loop
 *函数功能 : 协议轮询函数
 *输入参数 : void
 *输出返回 : void
*******************************************************************************/
void vPROTOCOL_Loop(void)
{

    if (read_success == 1)              //如果成功读取一帧数据
    {
        /*复位通信超时计数，并复位通信错误代码*/
        gusOvertimeCnt = 0;
        if(gxBikeinfo.CommunicationErr == 0x30)
        {
            gxBikeinfo.CommunicationErr = 0;
        }
        //一帧数据接收成功后先根据协议要求进行校验和，验证数据的正确性
        //如果数据正确，根据接收的数据进行分析获取需要的内容
        if (Check_Sum_Handle())
        {
            /*判断接收到的数据，前两个字节是否是0x08和0x61，进一步验证数据准确性*/
            if(receive_data_buf[0] == 0x08 && receive_data_buf[1] == 0x61)
            {
                gxBikeinfo.ErrCode = receive_data_buf[3];               /*错误代码*/
                gxBikeinfo.Mode    = receive_data_buf[4] & 0x03;        /*速度模式*/
                gxBikeinfo.Current = receive_data_buf[6];               /*电流，有符号类型*/
                gxBikeinfo.Speed_Cnt = ((uint16_t)receive_data_buf[7] << 8) | receive_data_buf[8]; /*该数据实际就是车轮转速，单位rpm*/
                gxBikeinfo.Soc     = receive_data_buf[9];
            }
        }

        read_success = 0;               //读取一帧数据清0
    }
    else
    {
        if(++gusOvertimeCnt > 10000 / LOOP_CYCLE)
        {
            gxBikeinfo.CommunicationErr = 0;//0x30;
        }
    }
}

/*******************************************************************************
 *函数名称 : SIF_GPIO_Init
 *函数功能 : 数据接收引脚初始化
 *输入参数 : void
 *输出返回 : void
*******************************************************************************/
void SIF_GPIO_Init(void)
{
    GPIO_InitTypeDef GPIO_InitStruct = {0};

    /*开启对应端口时钟*/
    __HAL_RCC_GPIOB_CLK_ENABLE();

    GPIO_InitStruct.Pin  = GPIO_PIN_7;
    GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
    GPIO_InitStruct.Pull = GPIO_PULLUP;
    HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);         /*PB7端口作为通信用端口*/
}

/*******************************************************************************
 *函数名称 : SIF_Timer_Init
 *函数功能 : 定时器初始化，单次定时时间根据协议的最小公差±50us确定
 *输入参数 : void
 *输出返回 : void
*******************************************************************************/
void SIF_Timer_Init(void)
{
    MX_TIM1_Init();
    HAL_TIM_Base_Start_IT(&htim1);
}

/*******************************************************************************
 *函数名称 : SIF_Timer_ISR_Callback
 *函数功能 : 定时器中断处理函数回调函数
 *输入参数 : void
 *输出返回 : void
*******************************************************************************/
void SIF_Timer_ISR_Callback(void) //500us定时器
{
    if (start_H_L_Level_timming_flag==1)
    {
        H_L_Level_time_cnt++;     //高低电平维持时间计数变量
    }
    Receive_Data_Handle();      //接收数据处理，波特率自适应
}



/*******************************************************************************
 *函数名称 : Receive_Data_Handle
 *函数功能 : 接收数据处理
 *输入参数 : void
 *输出返回 : void
*******************************************************************************/
static void Receive_Data_Handle(void)
{
    switch (receive_state)                          //检测当前接收数据状态
    {
    case INITIAL_STATE:                         //初始状态，未接收到同步信息，进行同步判断
    {
        if (DATA_REV_PIN == LOW)                //判断接收引脚的电平状态，当读到低电平时，开始计时
        {
            receive_bit_num = 0;                //重置bit位计数器
            receive_data_num = 0;               //重置接收数据个数
            H_L_Level_time_cnt = 0;             //高低电平计时变量清0
            start_H_L_Level_timming_flag = 1;   //开始高低电平计时
            receive_state = SYNC_L_STATE;       //进入读取同步低电平信号状态
        }
        break;
    }
    case SYNC_L_STATE:                          //在读取同步低电平信号期间
    {
        if (DATA_REV_PIN == HIGH)               //同步信号低电平检测期间读到高电平
        {
            if (H_L_Level_time_cnt >= SYNC_L_TIME_NUM)//如果同步信号低电平时间>=SYNC_L_TIME_NUM
            {   //同步信号低电平时间要>=10ms
                H_L_Level_time_cnt = 0;         //高低电平计时变量清0
                receive_state = SYNC_H_STATE;   //进入读取同步信号高电平状态
            }
            else
            {
                receive_state = RESTART_REV_STATE;      //进入重新接收状态
            }
        }
        break;
    }
    case SYNC_H_STATE:                          //在读取同步信号高电平期间
    {
        if (DATA_REV_PIN == LOW)                //同步信号高电平检测期间读到低电平
        {
            //判断同步信号高电平时间是否在1ms±100us之间
            if (H_L_Level_time_cnt >= SYNC_H_TIME_NUM_MIN && H_L_Level_time_cnt <= SYNC_H_TIME_NUM_MAX)
            {
                H_L_Level_time_cnt = 0;         //高低电平计时变量清0
                receive_state = DATA_REV_STATE; //进入读取数据状态
            }
            else
            {
                receive_state = RESTART_REV_STATE;      //进入重新接收状态
            }
        }
        else            //如果在同步信号高电平检测期间，时间超过2ms±200us，认为超时
        {
            //判断时间是否超时 2ms±200us
            if (H_L_Level_time_cnt >= LOGIC_CYCLE_NUM_MAX)
            {
                receive_state = RESTART_REV_STATE;      //进入重新接收状态
            }
        }
        break;
    }
    case DATA_REV_STATE:          //在读取数据码电平期间
    {
        //逻辑“0”为 1.5ms±150us低电平 + 0.5ms±50us高电平
        //逻辑“1”为 0.5ms±50us低电平 + 1.5ms±150us高电平
        //如何判断当前为逻辑“0”还是逻辑“1”，关键在于寻找共同点
        //方法一：
        //不管是逻辑“0”还是逻辑“1”，周期一样，都是2ms ± 200us
        //可以取中间时间点进行判断，(2ms ± 200us) / 2 = 1ms ± 100us
        //如果还没有读取一个bit位，且时间计数已经>=900us 且 <=1100us
        if ((has_read_bit==0) && (H_L_Level_time_cnt >= HALF_LOGIC_CYCLE_MIN && H_L_Level_time_cnt <= HALF_LOGIC_CYCLE_MAX))
        {
            receive_data_buf[receive_data_num] |= DATA_REV_PIN;
            has_read_bit = 1;
        }

        //方法二：
        //不管是逻辑“0”还是逻辑“1”，高低电平维持时间都是以 0.5ms±50us 为基数，
        //1.5ms±150us = 3 * (0.5ms±50us)，所以一个逻辑周期 2ms±200us = (1.5ms±150us) + (0.5ms±50us) = 4 * (0.5ms±50us)
        //所以可以取一个逻辑周期的中间时间断进行判断，即>(0.5ms±50us) 且 <(1.5ms±150us) 这段时间内判断，大于去上公差，小于取下公差
        //所以判断时间范围为 0.5ms+50us ~ 1.5ms-150us = 550us ~ 1350us
//            if ((has_read_bit==0) && (H_L_Level_time_cnt > SHORT_TIME_NUM_MAX) && (H_L_Level_time_cnt < LONG_TIME_NUM_MIX))
//            {
//                receive_data_buf[receive_data_num] |= DATA_REV_PIN;
//                has_read_bit = 1;
//            }

        //如果已经读取一个bit位，且时间计数已经>=2ms±200us，说明一个逻辑周期过去了
        if ((has_read_bit==1) && (H_L_Level_time_cnt >= LOGIC_CYCLE_NUM_MIN && H_L_Level_time_cnt <= LOGIC_CYCLE_NUM_MAX))
        {
            H_L_Level_time_cnt = 0;             //高低电平计时变量清0
            has_read_bit = 0;                   //清0，读取下一个bit位
            receive_bit_num++;                  //接收的bit数++

            if (receive_bit_num==REV_BIT_NUM)   //如果一个字节8个bit位接收完成
            {
                receive_data_num++;             //接收的数据个数++
                receive_bit_num = 0;            //接收bit位个数清0重新接收

                if (receive_data_num == REV_DATA_NUM)   //如果数据采集完毕
                {
                    receive_state = END_SIGNAL_STATE;   //进入接收结束低电平信号状态
                }
            }
            else                                //如果一个字节8个bit位还没有接收完成
            {
                //将接收数据缓存左移一位，数据从低bit位开始接收
                receive_data_buf[receive_data_num] = receive_data_buf[receive_data_num] >> 1;
            }
        }
        break;
    }
    case END_SIGNAL_STATE:                              //在接收结束信号低电平期间
    {
        if (DATA_REV_PIN == HIGH)                       //结束信号低电平检测期间读到高电平
        {
            if (H_L_Level_time_cnt >= END_SIGNAL_TIME_NUM)  //如果读到低电平时间>=5ms
            {
                read_success = 1;                   //一帧数据读取成功
                start_H_L_Level_timming_flag = 0;   //停止高低电平计时
                H_L_Level_time_cnt = 0;             //定时器计数值清0
                receive_state = INITIAL_STATE;      //接收状态清0
            }
            else        //如果低电平时间没有5ms
            {
                receive_state = RESTART_REV_STATE;      //进入重新接收状态
            }
        }
        else    //结束信号低电平检测期间一直为低
        {
            if (H_L_Level_time_cnt >= SYNC_L_TIME_NUM)  //如果读到低电平时间>=10ms，认为超时
            {   //一帧数据发送完成后需要间隔50ms才发送第二帧数据，期间肯定会被拉高
                receive_state = RESTART_REV_STATE;      //进入重新接收状态
            }
        }
        break;
    }
    case RESTART_REV_STATE:                     //重新接收数据状态
    {
        start_H_L_Level_timming_flag = 0;       //停止高低电平计时
        H_L_Level_time_cnt = 0;                 //定时器计数值清0
        receive_state = INITIAL_STATE;          //接收状态清0
        break;
    }
    }
}

/*******************************************************************************
 *函数名称 : Check_Sum_Handle
 *函数功能 : 所有数据的异或校验
 *输入参数 : void
 *输出返回 : 校验结果。0-校验失败，1-校验成功
*******************************************************************************/
static uint8_t Check_Sum_Handle(void)
{
    unsigned char i = 0, checkByte = 0;
    unsigned long checkSum = 0;

    for ( i = 0; i < (REV_DATA_NUM - 1); i++)
    {
        checkSum ^= receive_data_buf[i];
    }

    checkByte = (unsigned char)checkSum;

    if (checkByte == receive_data_buf[REV_DATA_NUM-1])  //校验正确
    {
        return 1;           //标记校验成功
    }
    else
    {
        return 0;           //标记校验失败
    }
}

/******************************************************************************
** 功  能：获取当前电流值
** 参  数：无
** 返回值：实际电流值。有符号型。
** 备  注：
******************************************************************************/
int8_t usPROTOCOL_Get_Current(void)
{
    return gxBikeinfo.Current;
}

/******************************************************************************
** 功  能：获取电量SOC
** 参  数：无
** 返回值：实际电量值，-1表示不支持SOC。
** 备  注：
******************************************************************************/
int8_t cPROTOCOL_Get_Soc(void)
{
    return gxBikeinfo.Soc;
}

/******************************************************************************
** 功  能：获取当前车速
** 参  数：无
** 返回值：实际车速*10.比如返回120，实际车速即12.0Km/h
** 备  注：
******************************************************************************/
uint16_t usPROTOCOL_Get_Speed(void)
{
    if(gxBikeinfo.CommunicationErr == 0 && gxBikeinfo.Wheel > 0)
    {
        if(gxBikeinfo.Wheel == 200)
        {
            /*20寸轮径，按照周长660mm计算。订单要求*/
            return 66UL * 314UL * 6UL * gxBikeinfo.Speed_Cnt / 100000;
        }
        else
        {
            return 4785U * gxBikeinfo.Wheel * gxBikeinfo.Speed_Cnt / 1000000;
        }
    }
    else
    {
        return 0;
    }
}

/******************************************************************************
** 功  能：获取错误代码
** 参  数：无
** 返回值：十六进制的错误代码
** 备  注：包括30错误代码（通信超时）
******************************************************************************/
uint8_t ucPROTOCOL_Get_Errcode(void)
{
    if(gxBikeinfo.CommunicationErr > 0)
    {
        return gxBikeinfo.CommunicationErr;
    }
    else
    {
        return gxBikeinfo.ErrCode;
    }
}

/******************************************************************************
** 功  能：获取速度模式
** 参  数：无
** 返回值：速度模式。0-无，1-低速，2-中速，3-高速
** 备  注：
******************************************************************************/
uint8_t ucPROTOCOL_Get_Mode(void)
{
    return gxBikeinfo.Mode;
}

/******************************************************************************
** 功  能：设置轮径
** 参  数：Wheel-轮径值
** 返回值：无
** 备  注：为了和其它协议兼容，传入的轮径值是实际值的10倍
******************************************************************************/
void vPROTOCOL_Set_Wheel(uint16_t Wheel)
{
    gxBikeinfo.Wheel = Wheel;
}


/******************************************************************************
** 功  能：开机上电时，设置默认要发送给控制器的数据
** 参  数：无
** 返回值：无
** 备  注：
******************************************************************************/
void vPROTOCOL_Set_Value_When_PowerOn(void)
{
    vPROTOCOL_Set_Wheel(usGet_Wheel_Size(gtParam.Wheel_Diameter));
}

/******************************************************************************
** 功  能：复位通信超时计数变量
** 参  数：无
** 返回值：无
** 备  注：
******************************************************************************/
void vPROTOCOL_ResetCommunicationOverTime(void)
{
    gusOvertimeCnt = 0;
}

/******************************************************************************
** 功  能：退出设置时，设置默认要发送给控制器的数据
** 参  数：无
** 返回值：无
** 备  注：
******************************************************************************/
void vPROTOCOL_Set_Value_When_ExitSetting(void)
{
    vPROTOCOL_Set_Wheel(usGet_Wheel_Size(gtParam.Wheel_Diameter));
}